package com.atguigu.gulimall.member.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.common.constant.AuthConstant;
import com.atguigu.gulimall.member.entity.MemberLevelEntity;
import com.atguigu.gulimall.member.exception.PhoneAlreadyExistException;
import com.atguigu.gulimall.member.exception.UsernameAlreadyExistException;
import com.atguigu.gulimall.member.service.MemberLevelService;
import com.atguigu.gulimall.member.vo.GiteeOAuth2RespVo;
import com.atguigu.gulimall.member.vo.RegisterVo;
import com.atguigu.gulimall.member.vo.UserLoginVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.member.dao.MemberDao;
import com.atguigu.gulimall.member.entity.MemberEntity;
import com.atguigu.gulimall.member.service.MemberService;

@Slf4j
@Service("memberService")
public class MemberServiceImpl extends ServiceImpl<MemberDao, MemberEntity> implements MemberService {

    @Autowired
    private MemberLevelService memberLevelService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<MemberEntity> page = this.page(
                new Query<MemberEntity>().getPage(params),
                new QueryWrapper<MemberEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 实际注册
     * @param registerVo
     */
    @Override
    public void register(RegisterVo registerVo) throws UsernameAlreadyExistException,PhoneAlreadyExistException{

        MemberEntity memberEntity = new MemberEntity();

        //查询设置默认会员等级
        MemberLevelEntity memberLevel = memberLevelService.getDefaultLevel();
        memberEntity.setLevelId(memberLevel.getId());

        //username和phone应该查询是否存在重复
        boolean existUsername = checkUsernameUnique(registerVo.getUsername());
        if (existUsername){
            throw new UsernameAlreadyExistException();
        }
        memberEntity.setUsername(registerVo.getUsername());

        boolean existPhone = checkPhoneUnique(registerVo.getPhone());
        if (existPhone){
            throw new PhoneAlreadyExistException();
        }
        memberEntity.setMobile(registerVo.getPhone());

        //密码须加密保存
        /**
         * 密码加密存储的思路
         * 1、两种加密方式，一种是可逆的，即可以通过密文破解出明文，一种是不可逆的，即即使知道密文也无法知道明文
         * 2、显然第二种更安全。验证时只需要把明文再加密一次去对比即可
         * 3、md5摘要算法就是不可逆的，用md5加密后存储是一种办法？
         * 4、不行，虽然md5是不可逆的，但是攻击者可以通过彩虹表猜出密码原文
         * 5、所以考虑加盐。这就好比是做菜，明文一样的情况下，md5每次加密出来都是一样的
         * 6、所以不妨加把盐，这样每次的口味轻重都有区别
         * 7、加盐也就是加上一个随机字符串跟明文拼接后再求md5值，由于彩虹表容量有限，加了随机字符的他很难统计到，这就增大了破解难度
         * 8、但是这样就还需要额外存当时的盐值，否则后续无法验证密码，稍显麻烦
         * 9、Spring给我们提供了一个 BCryptPasswordEncoder 可用于加密明文密码
         * 10、后续只需要再用matches方法来判断是否匹配即可
         * 11、BCryptPasswordEncoder加密出来的东西，即使每次明文都一样，密文也不相同，因为每次都加了随机的盐值
         * 12、那BCryptPasswordEncoder后续怎么验证呢？是因为他把盐值通过某种方式隐藏到了密文中
         * BCryptPasswordEncoder的使用很简单，每次要用new一个即可
         */

        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encodedPasswd = bCryptPasswordEncoder.encode(registerVo.getPassword());
        memberEntity.setPassword(encodedPasswd);
        this.save(memberEntity);
    }

    @Override
    public MemberEntity login(UserLoginVo userLoginVo) {
        MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>()
                .eq("username", userLoginVo.getLoginacct())
                .or().eq("mobile", userLoginVo.getLoginacct()));
        if (memberEntity == null){
            return null;
        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(userLoginVo.getPassword(), memberEntity.getPassword());
        if (!matches){
            return null;
        }

        return memberEntity;
    }

    private boolean checkUsernameUnique(String username) {
        Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("username", username));
        return count > 0;
    }

    private boolean checkPhoneUnique(String phone) {
        Integer count = baseMapper.selectCount(new QueryWrapper<MemberEntity>().eq("mobile", phone));
        return count > 0;
    }

    @Override
    public MemberEntity auth2Login(GiteeOAuth2RespVo giteeOAuth2RespVo) throws Exception {
        URL url = new URL("https://gitee.com/api/v5/user?access_token="+giteeOAuth2RespVo.getAccess_token());

        // 打开连接
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();

        // 设置请求方法为GET
        conn.setRequestMethod("GET");

        // 连接，并获取响应码，200表示成功
        int responseCode = conn.getResponseCode();

        if (responseCode == HttpURLConnection.HTTP_OK) {
            // 读取响应内容
            BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String inputLine;
            StringBuffer response = new StringBuffer();

            while ((inputLine = in.readLine()) != null) {
                response.append(inputLine);
            }
            in.close();

            JSONObject jsonObject = JSON.parseObject(response.toString());
            Integer id = jsonObject.getInteger("id");
            String name = jsonObject.getString("name");
            //通过id判断，如果没有则注册；有则登录，修改掉accessToken等信息

            MemberEntity memberEntity = this.baseMapper.selectOne(new QueryWrapper<MemberEntity>().eq("social_uid", id));
            if (memberEntity != null){
                //说明已经注册，须更新access_token等信息
                MemberEntity update = new MemberEntity();
                update.setId(memberEntity.getId());
                update.setAccessToken(giteeOAuth2RespVo.getAccess_token());
                update.setSocialUid(id);
                update.setExpireIn(giteeOAuth2RespVo.getExpires_in().toString());
                baseMapper.updateById(update);
                memberEntity.setAccessToken(giteeOAuth2RespVo.getAccess_token());
                memberEntity.setExpireIn(giteeOAuth2RespVo.getExpires_in().toString());
                return memberEntity;
            }else{
                MemberEntity insert = new MemberEntity();
                insert.setSocialUid(id);
                insert.setUsername(name);
                baseMapper.insert(insert);
                return insert;
            }
        } else {
            log.error("GET request not worked");
            return null;
        }
    }
}